/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield 
 *
 * This library is open source and may be redistributed and/or modified under  
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 * OpenSceneGraph Public License for more details.
*/

/* Note, elements of GraphicsWindowX11 have used Prodcer/RenderSurface_X11.cpp as both
 * a guide to use of X11/GLX and copiying directly in the case of setBorder().
 * These elements are license under OSGPL as above, with Copyright (C) 2001-2004  Don Burns.
 */

#ifndef OSGVIEWER_GRAPHICSWINDOWWAYLAND
#define OSGVIEWER_GRAPHICSWINDOWWAYLAND 1

#include <osgViewer/GraphicsWindow>
#include <osgViewer/api/Wayland/GraphicsHandleWayland>
#include <osg/Referenced>
#include <osg/ref_ptr>

#include <string.h>

#include <bitset>

typedef std::vector<EGLint> Attributes;

namespace osgViewer
{
    class WaylandWindow;
    
    class WLEglDisplay : public osg::Referenced
    {
    public:
        WLEglDisplay(struct wl_display* wl_display);
        ~WLEglDisplay();

        EGLDisplay getEglDisplay();
    private:
        EGLDisplay _eglDisplay;
    };
    
    class OSGVIEWER_EXPORT GraphicsWindowWayland : public osgViewer::GraphicsWindow, public osgViewer::GraphicsHandleWayland
    {
    public:
        GraphicsWindowWayland(osg::GraphicsContext::Traits* traits) : GraphicsWindow(),
           _valid(false),
           _initialized(false),
           _realized(false),
           _eglSurface(EGL_NO_SURFACE),
           _wl_native_window(NULL),
           _layerId(3000),
           _surfaceId(40),
           _successfulInitStage(-1),
           _wlEglDisplay(),
           _wlWindow(NULL),
           _lastTouchPoints(),
           _currentTouchContacts(0)
        {
            _traits = traits;
            
            init();
            
            if (valid())
            {
                setState( new osg::State );
                getState()->setGraphicsContext(this);
                
                if (_traits.valid() && _traits->sharedContext.valid())
                {
                    getState()->setContextID( _traits->sharedContext->getState()->getContextID() );
                    incrementContextIDUsageCount( getState()->getContextID() );   
                }
                else
                {
                    getState()->setContextID( osg::GraphicsContext::createNewContextID() );
                }
            }
        }
        
        virtual bool isSameKindAs(const Object* object) const { return dynamic_cast<const GraphicsWindowWayland*>(object)!=0; }
        virtual const char* libraryName() const { return "osgViewer"; }
        virtual const char* className() const { return "GraphicsWindowWayland"; }
        
        virtual bool valid() const { return _valid; }
        
        /** Realise the GraphicsContext.*/
        virtual bool realizeImplementation();
        
        /** Return true if the graphics context has been realised and is ready to use.*/
        virtual bool isRealizedImplementation() const { return _realized; }
        
        /** Close the graphics context.*/
        virtual void closeImplementation();
        
        /** Make this graphics context current.*/
        virtual bool makeCurrentImplementation();
        
        /** Release the graphics context.*/
        virtual bool releaseContextImplementation();
        
        /** Swap the front and back buffers.*/
        virtual void swapBuffersImplementation();
        
        virtual bool setWindowRectangleImplementation(int x, int y, int width, int height);
        
        virtual bool checkEvents();
        
        virtual void setWindowName(const std::string& winName);
        
        virtual void raiseWindow();
        
        virtual bool setSwapInterval(int intervalFrames) const;

        void setEventMask(uint32_t mask);
        
        EGLDisplay getEGLDisplay() const { return _wlEglDisplay->getEglDisplay(); }
        WLEglDisplay* getWLEglDisplay() { return _wlEglDisplay.get(); }
        
        WaylandWindow* getWLWindow() { return _wlWindow; }
        
        /** WindowData is used to pass in the wayland surfaceID and layerID attached the GraphicsContext::Traits structure.*/
        struct WindowData : public osg::Referenced
        {
            WindowData(uint32_t layerID, uint32_t surfaceID, uint32_t surfaceVisibility = 1U):
              _layerID(layerID), _surfaceID(surfaceID), _surfaceVisibility(surfaceVisibility) {}
        
            uint32_t    _layerID;
            uint32_t    _surfaceID;
            uint32_t    _surfaceVisibility;
        };
        
        void handleTouchDown(uint32_t time, int32_t id, float x, float y);
        void handleTouchUp(uint32_t time, int32_t id);
        void handleTouchMotion(uint32_t time, int32_t id, float x, float y);
        void transformMouseXY(float &x, float &y);
        
        void setSurfaceVisibility(uint32_t surfaceVisibility);

    protected:
        void init();
        virtual ~GraphicsWindowWayland();
		
        bool            	                   _valid;
        bool            	                   _initialized;
        bool            	                   _realized;
        
        EGLSurface      	                   _eglSurface;
        
        struct wl_egl_window*       	       _wl_native_window;
          
        unsigned int                           _width;
        unsigned int                           _height;
        unsigned int                           _layerId;
        unsigned int                           _surfaceId;
        int                                    _successfulInitStage;
        
    private:
        osg::ref_ptr<WLEglDisplay>             _wlEglDisplay;
        WaylandWindow*                         _wlWindow;
        
        void addKnownTouchPoints(osgGA::GUIEventAdapter *touchEvent, uint32_t skip);
        bool checkEGLError(const char* str);
        int createWindow(int width, int height, EGLConfig eglConfig);
        void destroyWindow();
        void getAttributes(Attributes *attrs, Traits *ctxTraits);
        
        // Maximum number of different touch points that will ever be reported
        // by Linux kernel. Maximum in our system (eGalax touch driver) is 5.
        static const int MAX_TOUCH_POINTS = 16;
        
        osg::Vec2 _lastTouchPoints[MAX_TOUCH_POINTS];
        std::bitset<MAX_TOUCH_POINTS> _currentTouchContacts;
    };
}

#endif

